//
// Created by ta&to on 2022/8/23.
//
#include <stdarg.h>
#include "tcp_log.h"

#define ACCESS_FILE "./logs/access.out"
#define ERROR_FILE  "./logs/error.out"

static file_n file;
static tcp_str_t err_levels[] = {
        tcp_null_string,
        tcp_string("critical"),
        tcp_string("error"),
        tcp_string("warning"),
        tcp_string("info"),
        tcp_string("debug")
};

/*
 * 创建一个日志任务容器
 */
static inline task_log_t *newlogcontainer(dts* data, struct file_s *file, int err)
{
    task_log_t *tlt;
    tlt = (task_log_t *)tcp_calloc(sizeof(task_log_t));
    tlt->next = NULL;
    tlt->data = data;
    tlt->file = file;
    tlt->err  = err;
}

static inline void destrylogcontainer(task_log_t *tlt)
{
    if(tlt) free(tlt);
    tlt = NULL;
}
/**
 * 创建一个任务队列
 * @param ttq 任务队列
 */
void Create_log_queue(log_task_queue_t *tlt)
{
    tlt->count = 0;
    tlt->first = NULL;
    tlt->last = &(tlt->first);
}
/**
 * 向队列中加入任务
 * @param ttq
 * @param task
 */
static inline void Insert_log_To_Queue(log_task_queue_t *ltq, task_log_t *task)
{
    if(!ltq || !task) return;
    if(ISNULL(ltq->first))
        ltq->first = task;
    *(ltq->last) = task;
    ltq->last = &task->next;
    atomicIncr(ltq->count, 1);
}

// 任务出队
static inline task_log_t* Get_Log_From_Queue(log_task_queue_t *ltq)
{
    task_log_t *task = ltq->first;
    if(ISNOTNULL(task))
    {
        ltq->first = task->next;
        if (ISNULL(ltq->first)) {
            ltq->last = &ltq->first;
        }
        atomicDecr(ltq->count, 1);
    }
    return task;
}

static inline void log_push(log_task_queue_t *ltq, char* logs, file_n *handler, int err)
{
    if(ISNULL(ltq) || ISNULL(logs))
        return;
    dts d = dtsnew(logs);
    if(ISNULL(d)) return;
    task_log_t *tlt = newlogcontainer(d, handler, err);
    Insert_log_To_Queue(ltq, tlt);
}
static inline task_log_t* log_pop(log_task_queue_t *ltq)
{
    if(ISNULL(ltq)) return NULL;
    return Get_Log_From_Queue(ltq);
}

void log_init(log_t *logs)
{
    logs->handler = NULL;
    tcp_time_init();
    file_n *errfile, *debugfile;
    errfile = (file_n *) tcp_calloc(sizeof(errfile));
    debugfile = (file_n *) tcp_calloc(sizeof(debugfile));
    errfile->fd = wfile(ERROR_FILE);
    logs->err_file = errfile;
    debugfile->fd =  wfile(ACCESS_FILE);
    logs->debug_file = debugfile;
    Create_log_queue(&logs->queue);
}
void log_write_core(int level, int err, log_t *logs,char *fmt, ...)
{
    va_list      args;
    char *p, *last, *msg;
    char errstr[MAX_ERROR_STR];
    memset(errstr, 0, MAX_ERROR_STR);
    last = errstr + MAX_ERROR_STR;
    tcp_time_update(); // 更新时间
    p = tcp_cpymem(errstr, tcp_cached_err_log_time.data, tcp_cached_err_log_time.len);
    p = tcp_slprintf(p, last, " [%V] ", &err_levels[level]);

    msg = p;

    va_start(args, fmt);
    p = tcp_vslprintf(p, last, fmt, args);
    va_end(args);
    if (level != LOG_DEBUG && logs->handler) {
        p = logs->handler(logs, p, last - p);
    }

    if (p > last - TCP_LINEFEED_SIZE) {
        p = last - TCP_LINEFEED_SIZE;
    }
    tcp_linefeed(p);

    msg -= (tcp_cached_err_log_time.len + err_levels[level].len + 4);

    log_push(&logs->queue, msg, err?logs->err_file:logs->debug_file, err);
}
void tcp_log_cycle(void *args, thread_task_args_t *args_t)
{
    log_t *logt = (log_t *)args;
    for (;;)
    {
        task_log_t *lt = log_pop(&logt->queue);// 取出一个日志任务
        if(ISNULL(lt)) continue;
        dts data = lt->data;
        tcp_write_file(lt->file, data, dtslen(data), NULL);
        tcp_write_console((lt->err?tcp_stderr:tcp_stdout), data, dtslen(data));
    }
}

